{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "91af3eba",
   "metadata": {},
   "outputs": [],
   "source": [
    "%use fuel(2.3.1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "a692f3da-8e2c-43f1-9fea-17d92f9909b9",
   "metadata": {},
   "outputs": [],
   "source": [
    "import java.util.Stack"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "d86fa867",
   "metadata": {},
   "outputs": [],
   "source": [
    "val envs = java.io.File(\"../../.env\")\n",
    "    .readLines()\n",
    "    .map {\n",
    "        it.split(\"=\")[0] to it.split(\"=\")[1].trim('\"')\n",
    "    }.toMap()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "65e62477",
   "metadata": {},
   "outputs": [],
   "source": [
    "val session = envs.get(\"AOC_SESSION\")\n",
    "val year = 2021\n",
    "val day = 18"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "a2bb91d0",
   "metadata": {},
   "outputs": [],
   "source": [
    "fun getInput(year: Int, day: Int, session: String): String {\n",
    "    val (_, _, result) = \"https://adventofcode.com/$year/day/$day/input\"\n",
    "    .httpGet()\n",
    "    .header(\"cookie\" to \"session=$session\")\n",
    "    .responseString()\n",
    "        \n",
    "    return result.get().trim()\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "f0917bbf",
   "metadata": {},
   "outputs": [],
   "source": [
    "fun submitAnswer(year: Int, day: Int, session: String, level: Int, answer: String): String {\n",
    "    val (_, _, result) = Fuel\n",
    "    .post(\n",
    "        \"https://adventofcode.com/$year/day/$day/answer\", \n",
    "        parameters = listOf(\"level\" to level, \"answer\" to answer))\n",
    "    .header(\"cookie\" to \"session=$session\")\n",
    "    .responseString()\n",
    "        \n",
    "    return result.get()\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "dd0e5a59",
   "metadata": {},
   "outputs": [],
   "source": [
    "val sample = \"\"\"[[[0,[5,8]],[[1,7],[9,6]]],[[4,[1,2]],[[1,4],2]]]\n",
    "[[[5,[2,8]],4],[5,[[9,9],0]]]\n",
    "[6,[[[6,2],[5,6]],[[7,6],[4,7]]]]\n",
    "[[[6,[0,7]],[0,9]],[4,[9,[9,0]]]]\n",
    "[[[7,[6,4]],[3,[1,3]]],[[[5,5],1],9]]\n",
    "[[6,[[7,3],[3,2]]],[[[3,8],[5,7]],4]]\n",
    "[[[[5,4],[7,7]],8],[[8,3],8]]\n",
    "[[9,3],[[9,9],[6,[4,9]]]]\n",
    "[[2,[[7,7],7]],[[5,8],[[9,3],[0,2]]]]\n",
    "[[[[5,2],5],[8,[3,7]]],[[5,[7,5]],[4,4]]]\"\"\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "83fc6186",
   "metadata": {},
   "outputs": [],
   "source": [
    "val input = getInput(year, day, session)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "de932d1f-642f-4bf1-afe1-f677f967ede4",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Node{\n",
    "    var num: Int = 0\n",
    "    var left: Node? = null\n",
    "    var right: Node? = null\n",
    "    var parent: Node? = null\n",
    "    \n",
    "    constructor (num: Int) {\n",
    "        this.num = num\n",
    "    }\n",
    "    \n",
    "    constructor (str: String) {\n",
    "        if (!str.startsWith(\"[\")) {\n",
    "            this.num = str.toInt()\n",
    "        } else {\n",
    "            val stk = Stack<Node>()\n",
    "            var numberStart = -1\n",
    "            for ((i, ch) in str.withIndex()) {      \n",
    "                when (ch) {\n",
    "                    '[' -> {\n",
    "                        if (stk.isEmpty())\n",
    "                            stk.add(this)\n",
    "                        else\n",
    "                            stk.add(Node(0))\n",
    "                    }\n",
    "                    ']' -> {\n",
    "                        val node = if (numberStart != -1) {\n",
    "                            Node(str.slice(numberStart until i))\n",
    "                        } else {\n",
    "                            stk.pop()!!\n",
    "                        }\n",
    "                        \n",
    "                        node.parent = stk.peek()\n",
    "                        if (node.parent!!.left == null)\n",
    "                            node.parent!!.left = node\n",
    "                        else \n",
    "                            node.parent!!.right = node\n",
    "                        \n",
    "                        numberStart = -1\n",
    "                    }\n",
    "                    ',' -> {\n",
    "                        val node = if (numberStart != -1) {\n",
    "                            Node(str.slice(numberStart until i))\n",
    "                        } else {\n",
    "                            stk.pop()!!\n",
    "                        }\n",
    "                        \n",
    "                        node.parent = stk.peek()\n",
    "                        node.parent!!.left = node\n",
    "                        numberStart = -1\n",
    "                    }\n",
    "                    else -> {\n",
    "                        if (numberStart == -1)\n",
    "                            numberStart = i\n",
    "                    }\n",
    "                }\n",
    "            }\n",
    "        }\n",
    "        \n",
    "        this.reduce()\n",
    "    }\n",
    "    \n",
    "    constructor (a: Node) {\n",
    "        this.num = a.num\n",
    "        if (a.left != null) {\n",
    "            this.left = Node(a.left!!)\n",
    "            this.left?.parent = this\n",
    "        }\n",
    "        if (a.right != null) {\n",
    "            this.right = Node(a.right!!)\n",
    "            this.right?.parent = this\n",
    "        }\n",
    "    }\n",
    "    \n",
    "    constructor (a: Node, b: Node) {\n",
    "        this.left = Node(a)\n",
    "        this.left!!.parent = this\n",
    "        this.right = Node(b)\n",
    "        this.right!!.parent = this\n",
    "        this.reduce()\n",
    "    }\n",
    "    \n",
    "    fun prevRegular(): Node? {\n",
    "        if (this.parent == null)\n",
    "            return null\n",
    "        \n",
    "        if (this == this.parent!!.left)\n",
    "            return this.parent!!.prevRegular()\n",
    "            \n",
    "        var ans = this.parent!!.left\n",
    "        while (ans!!.right != null)\n",
    "            ans = ans.right\n",
    "        \n",
    "        return ans\n",
    "    }\n",
    "    \n",
    "    fun nextRegular(): Node? {\n",
    "        if (this.parent == null)\n",
    "            return null\n",
    "        \n",
    "        if (this == this.parent!!.right)\n",
    "            return this.parent!!.nextRegular()\n",
    "            \n",
    "        var ans = this.parent!!.right\n",
    "        while (ans!!.left != null)\n",
    "            ans = ans!!.left\n",
    "        \n",
    "        return ans\n",
    "    }\n",
    "    \n",
    "    fun explode() {\n",
    "        val lValue = this.left!!.num\n",
    "        val rValue = this.right!!.num\n",
    "        val prev = this.prevRegular()\n",
    "        val next = this.nextRegular()\n",
    "        \n",
    "        if (prev != null)\n",
    "            prev.num += lValue\n",
    "        \n",
    "        if (next != null)\n",
    "            next.num += rValue\n",
    "        \n",
    "        this.left = null\n",
    "        this.right = null\n",
    "    }\n",
    "    \n",
    "    fun split() {\n",
    "        val lValue = this.num / 2\n",
    "        val rValue = this.num - lValue\n",
    "        this.num = 0\n",
    "        this.left = Node(lValue)\n",
    "        this.left?.parent = this\n",
    "        this.right = Node(rValue)\n",
    "        this.right?.parent = this\n",
    "    }\n",
    "    \n",
    "    fun checkExplosion(depth: Int, changed: BooleanArray) {\n",
    "        if (changed[0])\n",
    "            return\n",
    "        \n",
    "        if (depth >= 4 && this.left != null && this.left!!.left == null) {\n",
    "            changed[0] = true\n",
    "            this.explode()\n",
    "            return\n",
    "        }\n",
    "        \n",
    "        this.left?.checkExplosion(depth + 1, changed)\n",
    "        this.right?.checkExplosion(depth + 1, changed)\n",
    "    }\n",
    "    \n",
    "    fun checkSplit(changed: BooleanArray) {\n",
    "        if (changed[0])\n",
    "            return\n",
    "        \n",
    "        if (this.num >= 10) {\n",
    "            changed[0] = true\n",
    "            this.split()\n",
    "            return\n",
    "        }\n",
    "        \n",
    "        this.left?.checkSplit(changed)\n",
    "        this.right?.checkSplit(changed)\n",
    "    }\n",
    "        \n",
    "    fun reduce() {\n",
    "        val changed = BooleanArray(1)\n",
    "        this.checkExplosion(0, changed)\n",
    "        if (!changed[0])\n",
    "            this.checkSplit(changed)\n",
    "            \n",
    "        if (changed[0])\n",
    "            this.reduce()\n",
    "    }\n",
    "    \n",
    "    fun mag(): Int {\n",
    "        if (this.left == null)\n",
    "            return this.num \n",
    "        else \n",
    "            return 3 * this.left!!.mag() + 2 * this.right!!.mag()\n",
    "    }\n",
    "    \n",
    "    operator fun plus(other: Node): Node {\n",
    "        return Node(this, other)\n",
    "    }\n",
    "    \n",
    "    override fun toString(): String {\n",
    "        if (this.left == null) {\n",
    "            return this.num.toString()\n",
    "        } else {\n",
    "            return \"[\" + this.left?.toString() + \",\" + this.right?.toString() + \"]\"\n",
    "        }\n",
    "    }\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "c542c6ac",
   "metadata": {},
   "outputs": [],
   "source": [
    "fun partOne(input: String): String {\n",
    "    val nums = input.split(\"\\n\").map { Node(it) }\n",
    "    var ans = nums.drop(1).fold(nums[0]) {acc, it -> acc + it}\n",
    "    return ans.mag().toString()\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "cc01ce3c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4140"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "partOne(sample)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "a94d0dcb",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3756"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "val partOneAns = partOne(input)\n",
    "partOneAns"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6e0d4935",
   "metadata": {},
   "outputs": [],
   "source": [
    "submitAnswer(year, day, session, 1, partOneAns)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "9e7a88eb",
   "metadata": {},
   "outputs": [],
   "source": [
    "fun partTwo(input: String): String {\n",
    "    val nums = input.split(\"\\n\").map { Node(it) }\n",
    "    var ans = 0\n",
    "    \n",
    "    for (i in 0 until nums.size)\n",
    "        for (j in 0 until nums.size)\n",
    "            if (i != j)\n",
    "                ans = ans.coerceAtLeast((nums[i] + nums[j]).mag())\n",
    "    \n",
    "    return ans.toString()\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "261894c7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3993"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "partTwo(sample)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "11f62ebc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4585"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "val partTwoAns = partTwo(input)\n",
    "partTwoAns"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "899192ae",
   "metadata": {},
   "outputs": [],
   "source": [
    "submitAnswer(year, day, session, 2, partTwoAns)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Kotlin",
   "language": "kotlin",
   "name": "kotlin"
  },
  "language_info": {
   "codemirror_mode": "text/x-kotlin",
   "file_extension": ".kt",
   "mimetype": "text/x-kotlin",
   "name": "kotlin",
   "nbconvert_exporter": "",
   "pygments_lexer": "kotlin",
   "version": "1.6.20-dev-5432"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
